// Copyright (c) 2007, Brian Duff
// See LICENSE for license details.

package org.dubh.orablogs.ag;

import java.io.StringReader;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.ccil.cowan.tagsoup.Parser;

import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;


/**
 * Sanitizes a chunk of HTML to eliminate potential XSS and rendering issues.
 */
final class HtmlSanitizer
{
  private static final Logger LOG = Logger.getLogger( HtmlSanitizer.class.getName() );

  private final String _sourceUrl;
  
  public HtmlSanitizer( String sourceUrl )
  {

    _sourceUrl = sourceUrl;
  }
  
  /**
   * Attempt to sanitize the HTML text in the specified string.
   * 
   * @param text some text to sanitize.
   * @return sanitized text.
   */
  public String sanitize( String text )
  {
    if ( text == null || text.trim().length() == 0 ) return "<p><i>No summary available</i></p>";
    
    StringReader sr = null;
    try
    {
      Parser p = new Parser();
      FilterHandler handler = new FilterHandler();
      p.setContentHandler( handler );
      sr = new StringReader( text );
      p.parse( new InputSource( sr ));
      
      return handler.getResult();
    }
    catch ( Exception e )
    {
      LOG.log( Level.SEVERE, "Failed to parse entry text from " + _sourceUrl, e );
      LOG.info( text );
      return "<p><i>Orablogs could not read this entry because it contains malformed HTML.</i></p>";
    }
    finally
    {
      if ( sr != null ) sr.close();
    }
    
  }
  

  private class FilterHandler extends DefaultHandler
  {
    private final StringBuilder _result = new StringBuilder();
    
    public String getResult()
    {
      return _result.toString();
    }
    
    private boolean isWhiteElement( String localName )
    {
      return HTML_ELEMENT_WHITELIST.contains( localName );
    }
    
    private boolean isWhiteAttribute( String attributeName )
    {
      return HTML_ATTRIBUTE_WHITELIST.contains( attributeName );
    }
    
    @Override
    public void startElement(String uri, String localName, String qName,
                             Attributes attributes)
      throws SAXException
    {
      if ( !isWhiteElement( localName ) ) return;
      
      _result.append( "<" );
      _result.append( localName );
      for ( int i=0; i < attributes.getLength(); i++ )
      {
        String name = attributes.getLocalName( i );
        if ( !isWhiteAttribute( name ) ) continue;
        String value = attributes.getValue( i );
        
        if ( name == null || value == null ) continue;        
        _result.append( " " );
        _result.append( name );
        _result.append( "=\"" );
        _result.append( value );
        _result.append( "\"" );
      }
      _result.append( ">" );
    }

    @Override
    public void endElement(String uri, String localName, String qname )
      throws SAXException
    {
      if ( !isWhiteElement( localName ) ) return;
      
      _result.append( "</" );
      _result.append( localName );
      _result.append( ">" );
    }

    @Override
    public void characters(char[] c, int i, int i2)
      throws SAXException
    {
      _result.append( c, i, i2 );
    }

    @Override
    public void startDocument() throws SAXException
    {
      _result.append( "<div>" );
    }

    @Override
    public void endDocument()
      throws SAXException
    {
      _result.append( "</div>" );
    }
  }

  private static final Set<String> HTML_ELEMENT_WHITELIST = 
  new HashSet<String>( Arrays.asList( new String[]
  {
    "p", "br", "b", "i", "strong", "cite", "blockquote", "img", 
    "table", "tr", "td", "th", "a", "div", "span", "abbr", "acronym", 
    "code", "pre", "strike", "tbody", "u", "ul", "li", "tfoot", "em", "kbd"
  } ));
  
  private static final Set<String> HTML_ATTRIBUTE_WHITELIST =
  new HashSet<String>( Arrays.asList( new String[]
  {
    "src", "align", "href", "border", "width", "height", "valign", "title", 
    "alt"
  }));
}
